package lpg.tool.templatec;

enum State
{
    ACTION("<%", "<\\%")
    {
        protected State getNextState(Control control)
        {
            if (control.columnNo == control.line.length())
            {
                // <%EOL SCRIPTLET
                return SCRIPTLET;
            }
            char c = control.line.charAt(control.columnNo);
            switch (c)
            {
                case '=':
                    control.columnNo++;
                    // <%= EXPRESSION
                    return EXPRESSION;
                case '!':
                    control.columnNo++;
                    // <%! DECLARATION
                    return DECLARATION;
                case '@':
                    control.columnNo++;
                    // <%@ DIRECTIVE
                    return DIRECTIVE;
                case '-':
                    int index = control.columnNo + 1;
                    if (index < control.line.length() && control.line.charAt(index) == '-')
                    {
                        control.columnNo += 2;
                        // <%-- COMMENT
                        return COMMENT;
                    }
                default:
                    // <% SCRIPTLET
                    return SCRIPTLET;
            }
        }
    },

    COMMENT("--%>", null)   // <%--   --%>
    {
        void end(Control control, String str)
        {
            // Ignore comments
        }
    },

    DIRECTIVE               // <%@      %>
    {
        void end(Control control, String str)
        {
            control.parseDirective(unescape(str));
        }
    },

    EXPRESSION,             // <%=      %>
    DECLARATION,            // <%!      %>
    SCRIPTLET;              // <%       %>

    State()
    {
        this("%>", "%\\>");
    }

    State(String endTag, String escapeTag)
    {
        this.endTag     = endTag;
        this.len        = endTag.length();
        this.escapeTag  = escapeTag;
    }

    protected State getNextState(Control control)
    {
        return ACTION;
    }

    protected String unescape(String str)
    {
        StringBuilder buf = new StringBuilder();
        int pos = 0, index;
        while ((index = str.indexOf(escapeTag, pos)) >= 0)
        {
            buf.append(str.substring(pos, index));
            buf.append(endTag);
            pos = index + 3; // The lengths of all escape tags are 3
        }
        buf.append(str.substring(pos));
        return new String(buf);
    }

    State parse(Control control)
    {
        int index = control.line.indexOf(endTag, control.columnNo);
        // If EOL, process next line
        if (index == -1)
        {
            control.bufferAppend(control.line.substring(control.columnNo), this);
            control.columnNo = -1;  // Indicates EOL
            return this;
        }
        // Otherwise, do state transition
        control.bufferAppend(control.line.substring(control.columnNo, index), this);
        control.endColumnNo = index;
        control.columnNo = index + len;
        return getNextState(control);
    }

    void end(Control control, String str)
    {
        control.parseElement(unescape(str), this);
    }

    String getEndTag()
    {
        return endTag;
    }

    private final String    endTag;
    private final int       len;
    private final String    escapeTag;

}
